package org.dolphin.service.impl;

import java.io.Serializable;
import java.math.BigDecimal;
import java.sql.Date;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import javax.annotation.Resource;
import org.dolphin.base.bean.FlatProductAttributeVO;
import org.dolphin.base.bean.FlatProductItemSearchVO;
import org.dolphin.base.bean.FlatProductItemVO;
import org.dolphin.base.bean.FlatProductVO;
import org.dolphin.base.bean.ProductAttributePriceVO;
import org.dolphin.base.bean.ProductAttributeVO;
import org.dolphin.base.bean.ProductCategoryVO;
import org.dolphin.base.bean.ProductVO;
import org.dolphin.dao.ProductAttributeDao;
import org.dolphin.dao.ProductCategoryDao;
import org.dolphin.dao.ProductDao;
import org.dolphin.dao.SeasonalPromotionDao;
import org.dolphin.dao.UomDao;
import org.dolphin.entity.inventory.Product;
import org.dolphin.entity.inventory.ProductAttribute;
import org.dolphin.entity.inventory.ProductCategory;
import org.dolphin.entity.inventory.ProductCategoryLink;
import org.dolphin.entity.inventory.ProductItemStock;
import org.dolphin.entity.promo.ProductAttributeSeasonalPromotion;
import org.dolphin.entity.promo.ProductSeasonalPromotion;
import org.dolphin.service.ProductService;
import org.dolphin.util.Pagination;
import org.dolphin.service.base.UserProfileHelper;
import org.hibernate.criterion.Order;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@Service(value = "productService")
@Transactional(readOnly = true, propagation = Propagation.REQUIRED)
public class ProductServiceImpl implements ProductService {
  @Autowired(required = true)
  private ProductCategoryDao productCategoryDao;
  @Autowired(required = true)
  private ProductDao productDao;
  @Resource(name = "messageResource")
  private MessageSource messageSource;
  @Autowired(required = true)
  private ProductAttributeDao productAttributeDao;
  @Autowired(required = true)
  private UomDao uomDao;
  @Autowired(required = true)
  private SeasonalPromotionDao seasonalPromotionDao;

  @Transactional(readOnly = false)
  @Override
  public void createProduct(Product product) {
    String name = product.getName();
    Product p = productDao.getProduct(name);
    Locale locale = UserProfileHelper.getUserLocale();
    if (p != null) {
      String field = messageSource.getMessage("field.product.name", null, locale);
      String error = messageSource.getMessage("error.not.available", new Object[] {field}, locale);
      throw new IllegalArgumentException(error);
    }
    List<ProductAttribute> productAttributes = product.getProductAttributes();
    if (productAttributes != null && !productAttributes.isEmpty()) {
      for (ProductAttribute attribute : productAttributes) {
        String sku = attribute.getStockKeepingUnit();
        ProductAttribute pa = productAttributeDao.getProductAttribute(sku);
        if (pa != null) {
          String field = messageSource.getMessage("field.stock.keeping.unit", null, locale);
          String error = messageSource.getMessage("error.not.available", new Object[] {field},
              locale);
          throw new IllegalArgumentException(error);
        }
        ProductItemStock stock = new ProductItemStock();
        stock.setAttribute(attribute);
        stock.setQty(BigDecimal.ZERO);
        attribute.setProductItemStock(stock);
      }
    }
    productDao.saveProduct(product);
  }

  @Transactional(readOnly = false)
  @Override
  public void createProductAttribute(ProductAttribute productAttribute) {
    String sku = productAttribute.getStockKeepingUnit();
    ProductAttribute pa = productAttributeDao.getProductAttribute(sku);
    if (pa != null) {
      Locale locale = UserProfileHelper.getUserLocale();
      String field = messageSource.getMessage("field.stock.keeping.unit", null, locale);
      String error = messageSource.getMessage("error.not.available", new Object[] {field}, locale);
      throw new IllegalArgumentException(error);
    }
    ProductItemStock stock = new ProductItemStock();
    stock.setAttribute(productAttribute);
    stock.setQty(BigDecimal.ZERO);
    productAttribute.setProductItemStock(stock);
    productAttributeDao.saveProductAttribute(productAttribute);
  }

  @Transactional(readOnly = false)
  @Override
  public void createProductCategory(ProductCategory category) {
    String name = category.getName();
    ProductCategory pc = productCategoryDao.getProductCategory(name);
    if (pc != null) {
      Locale locale = UserProfileHelper.getUserLocale();
      String field = messageSource.getMessage("field.product.category.name", null, locale);
      String error = messageSource.getMessage("error.not.available", new Object[] {field}, locale);
      throw new IllegalArgumentException(error);
    }
    productCategoryDao.saveProductCategory(category);
  }

  @Override
  public Product fetchProduct(Serializable id) {
    Product product = productDao.fetchProduct(id, true);
    return product;
  }

  @Override
  public ProductAttribute fetchProductAttribute(Serializable id) {
    ProductAttribute attribute = productAttributeDao.fetchProductAttribute(id);
    return attribute;
  }

  @Override
  public ProductCategory fetchProductCategory(Serializable id) {
    ProductCategory productCategory = productCategoryDao.fetchProductCategory(id);
    return productCategory;
  }

  @Override
  public ProductVO getProduct(Serializable id) {
    boolean fetchProductCategory = true;
    Product product = productDao.fetchProduct(id, fetchProductCategory);
    ProductVO vo = toProduct(product, fetchProductCategory);
    return vo;
  }

  @Override
  public ProductAttributeVO getProductAttribute(Serializable id) {
    ProductAttribute productAttribute = productAttributeDao.fetchProductAttribute(id);
    ProductAttributeVO vo = toProductAttribute(productAttribute);
    return vo;
  }

  @Override
  public List<FlatProductAttributeVO> getProductAttributes(Serializable productId, int page,
      int rows, List<Order> orders) {
    page = page > 0 ? page : 1;
    int startIndex = Pagination.calculateStartIndex(page, rows);
    List<FlatProductAttributeVO> productAttributes = productAttributeDao.getProductAttributes(
        productId, startIndex, rows, orders);
    return productAttributes;
  }

  @Override
  public Long getProductAttributesCount(Serializable productId) {
    Long count = productAttributeDao.getProductAttributesCount(productId);
    return count;
  }

  @Override
  public List<ProductCategoryVO> getProductCategories(int page, int rows, List<Order> orders) {
    page = page > 0 ? page : 1;
    int startIndex = Pagination.calculateStartIndex(page, rows);
    List<ProductCategoryVO> list = productCategoryDao
        .getProductCategories(startIndex, rows, orders);
    return list;
  }

  @Override
  public Long getProductCategoriesCount() {
    Long count = productCategoryDao.getProductCategoriesCount();
    return count;
  }

  @Override
  public ProductCategoryVO getProductCategory(Serializable id) {
    ProductCategory productCategory = productCategoryDao.fetchProductCategory(id);
    ProductCategoryVO vo = toProductCategory(productCategory);
    return vo;
  }

  @Override
  public List<FlatProductItemVO> getProductItems(int page, int rows, List<Order> orders) {
    page = page > 0 ? page : 1;
    int startIndex = Pagination.calculateStartIndex(page, rows);
    List<FlatProductItemVO> productItems = productAttributeDao.getProductItems(startIndex, rows,
        orders);
    return productItems;
  }

  @Override
  public Long getProductItemsCount() {
    Long count = productAttributeDao.getProductItemsCount();
    return count;
  }

  @Override
  public List<FlatProductItemSearchVO> getProductItemSearch(int page, int rows, List<Order> orders) {
    page = page > 0 ? page : 1;
    int startIndex = Pagination.calculateStartIndex(page, rows);
    List<FlatProductItemSearchVO> list = productAttributeDao.getProductItemSearch(startIndex, rows,
        orders);
    return list;
  }

  @Override
  public Long getProductItemSearchCount() {
    Long records = productAttributeDao.getProductItemSearchCount();
    return records;
  }

  @Override
  public List<FlatProductVO> getProducts(int page, int rows, List<Order> orders) {
    page = page > 0 ? page : 1;
    int startIndex = Pagination.calculateStartIndex(page, rows);
    List<FlatProductVO> products = productDao.getProducts(startIndex, rows, orders);
    return products;
  }

  @Override
  public Long getProductsCount() {
    Long count = productDao.getProductsCount();
    return count;
  }

  @Override
  public Product loadProduct(Serializable id) {
    Product product = productDao.loadProduct(id);
    return product;
  }

  @Override
  public ProductAttribute loadProductAttribute(Serializable id) {
    ProductAttribute productAttribute = productAttributeDao.loadProductAttribute(id);
    return productAttribute;
  }

  @Override
  public ProductCategory loadProductCategory(Serializable id) {
    ProductCategory category = productCategoryDao.loadProductCategory(id);
    return category;
  }

  @Override
  public ProductAttributePriceVO lookupProductAttributePrice(ProductAttribute productAttribute,
      Date priceItemDate) {
    ProductAttribute attribute = productAttributeDao.fetchProductAttribute(
        productAttribute.getId(), true);
    Product product = attribute.getProduct();
    BigDecimal normalPrice = lookupProductRetailPrice(attribute);
    BigDecimal sellingPrice = lookupProductSellingPrice(attribute, priceItemDate);
    String name = product.getName() + " " + attribute.getName();
    ProductAttributePriceVO vo = new ProductAttributePriceVO();
    vo.setDescription(name.trim());
    vo.setProductAttributeId(attribute.getId());
    vo.setNormalPrice(normalPrice);
    vo.setSellingPrice(sellingPrice);
    return vo;
  }

  protected BigDecimal lookupProductRetailPrice(ProductAttribute productAttribute) {
    Product product = productAttribute.getProduct();
    BigDecimal baseRetailPrice = product.getBaseRetailPrice();
    if (baseRetailPrice.compareTo(BigDecimal.ZERO) <= 0) {
      String error = messageSource.getMessage("error.price.not.found", null,
          UserProfileHelper.getUserLocale());
      throw new IllegalArgumentException(error);
    }
    BigDecimal adjustmentRetailPrice = productAttribute.getAdjustmentRetailPrice();
    BigDecimal price = baseRetailPrice.add(adjustmentRetailPrice);
    return price;
  }

  protected BigDecimal lookupProductSellingPrice(ProductAttribute productAttribute,
      Date priceItemDate) {
    ProductAttributeSeasonalPromotion productAttributeSeasonalPromotion = seasonalPromotionDao
        .getPromotion(productAttribute, priceItemDate);
    if (productAttributeSeasonalPromotion != null) {
      return productAttributeSeasonalPromotion.getPromotionPrice();
    }
    ProductSeasonalPromotion productSeasonalPromotion = seasonalPromotionDao.getPromotion(
        productAttribute.getProduct(), priceItemDate);
    if (productSeasonalPromotion != null) {
      return productSeasonalPromotion.getPromotionPrice();
    }
    BigDecimal price = lookupProductRetailPrice(productAttribute);
    if (price.compareTo(BigDecimal.ZERO) <= 0) {
      String error = messageSource.getMessage("error.price.not.found", null,
          UserProfileHelper.getUserLocale());
      throw new IllegalArgumentException(error);
    }
    return price;
  }

  protected ProductVO toProduct(Product product, boolean fetchProductCategory) {
    ProductVO vo = new ProductVO();
    vo.setId(product.getId());
    vo.setName(product.getName());
    vo.setVersion(product.getVersion());
    vo.setBaseRetailPrice(product.getBaseRetailPrice());
    if (fetchProductCategory) {
      List<ProductCategoryLink> links = product.getProductCategoryLinks();
      if (links != null && !links.isEmpty()) {
        List<ProductCategoryVO> categoryList = new ArrayList<ProductCategoryVO>(links.size());
        for (ProductCategoryLink link : links) {
          ProductCategory category = link.getCategory();
          ProductCategoryVO productCategory = toProductCategory(category);
          categoryList.add(productCategory);
        }
        vo.setCategories(categoryList);
      }
    }
    return vo;
  }

  protected ProductAttributeVO toProductAttribute(ProductAttribute productAttribute) {
    ProductAttributeVO vo = new ProductAttributeVO();
    Product product = productAttribute.getProduct();
    vo.setId(productAttribute.getId());
    vo.setAttributeName(productAttribute.getName());
    vo.setProductId(product.getId());
    vo.setProductName(product.getName());
    vo.setBaseRetailPrice(product.getBaseRetailPrice());
    vo.setSku(productAttribute.getStockKeepingUnit());
    vo.setVersion(productAttribute.getVersion());
    vo.setAdjustmentRetailPrice(productAttribute.getAdjustmentRetailPrice());
    return vo;
  }

  protected ProductCategoryVO toProductCategory(ProductCategory productCategory) {
    ProductCategoryVO vo = new ProductCategoryVO();
    vo.setName(productCategory.getName());
    vo.setDescription(productCategory.getDescription());
    vo.setVersion(productCategory.getVersion());
    vo.setId(productCategory.getId());
    return vo;
  }

  @Transactional(readOnly = false)
  @Override
  public void updateProduct(Product product) {
    String id = product.getId();
    String name = product.getName();
    Product p = productDao.getProduct(name, id);
    if (p != null) {
      String field = messageSource.getMessage("field.product.name", null,
          UserProfileHelper.getUserLocale());
      String error = messageSource.getMessage("error.not.available", new Object[] {field},
          UserProfileHelper.getUserLocale());
      throw new IllegalArgumentException(error);
    }
    productDao.updateProduct(product);
  }

  @Transactional(readOnly = false)
  @Override
  public void updateProductAttribute(ProductAttribute productAttribute) {
    String sku = productAttribute.getStockKeepingUnit();
    String id = productAttribute.getId();
    ProductAttribute pa = productAttributeDao.getProductAttribute(sku, id);
    if (pa != null) {
      String field = messageSource.getMessage("field.stock.keeping.unit", null,
          UserProfileHelper.getUserLocale());
      String error = messageSource.getMessage("error.not.available", new Object[] {field},
          UserProfileHelper.getUserLocale());
      throw new IllegalArgumentException(error);
    }
    productAttributeDao.updateProductAttribute(productAttribute);
  }

  @Transactional(readOnly = false)
  @Override
  public void updateProductCategory(ProductCategory category) {
    String id = category.getId();
    String name = category.getName();
    ProductCategory pc = productCategoryDao.getProductCategory(name, id);
    if (pc != null) {
      String field = messageSource.getMessage("field.product.category.name", null,
          UserProfileHelper.getUserLocale());
      String error = messageSource.getMessage("error.not.available", new Object[] {field},
          UserProfileHelper.getUserLocale());
      throw new IllegalArgumentException(error);
    }
    productCategoryDao.updateProductCategory(category);
  }
}
